
export class KeyHelper {
    keyList:string[] = []
    keyListUp:string[] = []
    excludeDefaultBehavior: (()=> boolean)[] = []
    combinationKeyArray:{
        [k:string]: {
            keys: string[]
            count: number
            current: number
            is: boolean
        }
    } = {}

    changeEventList:((key: string, keyMode: 'keydown' | 'keyup')=>void)[] = []

    keydownEvent: any
    keyupEvent: any

    constructor(dom:(HTMLElement | SVGElement) & { [k: string]: any; } = document.body) {
        this.keydownEvent = this.#keydownEvent.bind(this)
        this.keyupEvent = this.#keyupEvent.bind(this)
        dom.addEventListener("keydown", this.keydownEvent )
        dom.addEventListener("keyup", this.keyupEvent)
    }

    #keydownEvent(e:KeyboardEvent) {
        let key = e.key.toLocaleLowerCase()

        if (key == " ") key = "space";
        if (this.keyList.indexOf(key) > -1) return
        this.keyList.push(key);

        Object.keys(this.combinationKeyArray).forEach(k => {
            const obj = this.combinationKeyArray[k]
            obj.is = true && obj.keys.length === this.keyList.length && obj.keys.indexOf(key) > -1
            for (let i = 0; i < obj.keys.length; i++) {
                obj.is = obj.is && this.isKeyDown(obj.keys[i])
            }
        })

        this.changeEventList.forEach(item => item(key, 'keydown'))

        for (let i = 0; i < this.excludeDefaultBehavior.length; i++) {
            const fun = this.excludeDefaultBehavior[i]
            if (fun()) {
                e.preventDefault()
                break
            }
        }
    }

    #keyupEvent(e:KeyboardEvent){
        let key = e.key.toLocaleLowerCase()
        if (key == " ") key = "space"
        const index = this.keyList.indexOf(key)
        if (index > -1) {
            this.keyList.splice(index, 1)
        }
        this.keyListUp.push(key)

        Object.keys(this.combinationKeyArray).forEach(k => {
            const obj = this.combinationKeyArray[k]
            obj.is = true && obj.keys.length === this.keyList.length && obj.keys.indexOf(key) > -1
            for (let i = 0; i < obj.keys.length; i++) {
                obj.is = obj.is && this.isKeyDown(obj.keys[i])
            }
        })

        setTimeout(() => {
            const index = this.keyListUp.indexOf(key)
            if (index > -1) {
                this.keyListUp.splice(index, 1)
            }
        }, 50)

        this.changeEventList.forEach(item => item(key, 'keyup'))
    }

    /**
     * 判断当前按键是否按下
     * @param key 
     * @returns 
     */
    isKeyDown(key: string) {
        if (this.keyList.indexOf(key) > -1) {
            return true
        }

        return false
    }

    
    /**
     * 判断当前按键是否抬起
     * @param key 
     * @returns 
     */
    isKeyUp(key: string) {
        if (this.keyListUp.indexOf(key) > -1) {
            return true
        }

        return false
    }

    /**
     * 判断当前按下的键是否只有一个
     * @returns 
     */
    isOneKey() {
        return this.keyList.length === 1
    }

    clear() {
        this.keyListUp.length = 0
    }

    /**
     * 定义一个针对浏览器默认行为的一个处理函数，返回值true为阻止浏览器默认行为
     * @param fun 
     */
    setExcludeDefaultBehavior(fun: ()=> boolean) {
        this.excludeDefaultBehavior.push(fun.bind(this))
    }

    /**
     * 创建一个组合按键
     * @param name 名称
     * @param keyArray 按键组，如['control','s'],常用的保存组合键 
     * @param count 按下组合键后的触发次数
     */
    combinationKey(name: string, keyArray:string[] = [], count = 1) {
        this.combinationKeyArray[name] = {
            keys: keyArray,
            count: Math.max(1, count),
            current: 0,
            is: false
        }
    }

    /**
     * 判断是否是组合键
     * @param name 自定义的组合按键名称
     * @returns 
     */
    isCombinationKey(name: string) {
        const obj = this.combinationKeyArray[name]

        if (!obj) return false

        const bool = obj.is

        if (bool) {
            if (obj.current != 0) {
                obj.current--;
                return true;
            }
            else {
                return false;
            }
        }
        else {
            obj.current = obj.count;
            return false;
        }
    }

    destory() {
    }

    getKeyList() {
        return this.keyList
    }

    addChangeEventListener(callBack: (key: string, keyMode: 'keydown' | 'keyup')=>void) {
        this.changeEventList.push(callBack)
    }

    static Q = "q"
    static W = "w"
    static E = "e"
    static R = "r"
    static T = "t"
    static Y = "y"
    static U = "u"
    static I = "i"
    static O = "o"
    static P = "p"
    static A = "a"
    static S = "s"
    static D = "d"
    static F = "f"
    static G = "g"
    static H = "h"
    static J = "j"
    static K = "k"
    static L = "l"
    static Z = "z"
    static X = "x"
    static C = "c"
    static V = "v"
    static B = "b"
    static N = "n"
    static M = "m"
    static CTRL = "control"
    static SHIFT = "shift"
    static CAPSLOCK = "capslock"
    static TAB = "tab"
    static ESCAPE = "escape"
    static META = "meta"
    static ALT = "alt"
    static SPACE = "space"
    static UP = "arrowup"
    static DOWN = "arrowdown"
    static LEFT = "arrowleft"
    static RIGHT = "arrowright"
    static ENTER = "enter"
    static BACKSPACE = "backspace"
    static F1 = "f1"
    static F2 = "f2"
    static F3 = "f3"
    static F4 = "f4"
    static F5 = "f5"
    static F6 = "f6"
    static F7 = "f7"
    static F8 = "f8"
    static F9 = "f9"
    static F10 = "f10"
    static F11 = "f11"
    static F12 = "f12"
    static DELETE = "delete"
    static PAUSE = "pause"
    static HOME = "home"
    static NUMLOCK = "numlock"
    static END = "end"
    static PAGEUP = "pageup"
    static PAGEDOWN = "pagedown"
    static INSERT = "insert"
}